#!/usr/bin/env python
# -*- coding: ISO-8859-1 -*-
#
#      Kovan's OGBot
#      Copyright (c) 2007 by kovan 
#
#      *************************************************************************
#      *                                                                       *
#      * This program is free software; you can redistribute it and/or modify  *
#      * it under the terms of the GNU General Public License as published by  *
#      * the Free Software Foundation; either version 2 of the License, or     *
#      * (at your option) any later version.                                   *
#      *                                                                       *
#      *************************************************************************
#

# python library:
import pdb
import sys, os

sys.path.insert(0, 'src')
sys.path.insert(0, 'lib')

if os.getcwd().endswith("src"):
    os.chdir("..")    

import logging, logging.handlers
import threading
import traceback
from Queue import *
import copy
import shutil
import cPickle
import urllib2
import itertools
import random
import warnings
import math
import pprint
from datetime import *
#from time import time, strptime, strftime
from optparse import OptionParser
from itertools import * 

# bot classes:

from GameEntities import *
from CommonClasses import *
from WebAdapter import WebAdapter
from Constants import *
            

from datetime import time

class BotConfig(object):
    def __init__(self):
        self.config = BotConfiguration(FILE_PATHS['config'])
        self.config.load()
        
class Bot(object):
    """Contains the bot logic, independent from the communications with the server.
    Theorically speaking if ogame switches from being web-based to being p.e. telnet-based
    this class should not be touched, only WebAdapter """
    class EventManager(BaseEventManager):
        ''' Displays events in console, logs them to a file or tells the gui about them'''
        def __init__(self, gui = None):
            super(Bot.EventManager, self).__init__(gui)
            
        def fatalException(self, exception):
            self.logAndPrint("Fatal error found, terminating. %s" % exception)
            self.dispatch("fatalException", exception)
        # new GUI messages:
        def connected(self):
            self.logAndPrint("OK")
            self.dispatch("connected")
        def simulationsUpdate(self, rentabilities):
            self.dispatch("simulationsUpdate", rentabilities)
        def activityMsg(self, msg):
            #self.logAndPrint(msg)
            msg = datetime.now().strftime("%X %x ") + msg
            self.dispatch("activityMsg", msg)
        def statusMsg(self, msg):
            self.dispatch("statusMsg", msg)
            
    def __init__(self, gui = None):   #only non-blocking tasks should go in constructor
        self.gui = gui
        self.msgQueue = Queue()
        self.eventMgr = Bot.EventManager(gui)
        self._planetDb = PlanetDb(FILE_PATHS['planetdb'])
        self.config = BotConfiguration(FILE_PATHS['config'])                   
        self.web = None
        self.myPlanets = []
        self.sourcePlanets = []
        self.researchLevels = {}
        self.newScanedPlanets = []
        self.config.load()
        self.inactivePlanets = []
        self.planetsMayError = []  #this is a tempor solution for avoid loop lanuch spy and attack mission error
        self.notArrivedAttacks = []
        self.sourcePlanetsDict = {}
        self.myPlanetsAvaliableTime = {}
        self.inactivePlanetsByCoords = {}
        self.reachableSolarSystems = []
        self._notArrivedEspionages = {}
        self.freeSlotTime = datetime(2000, 1, 1)
        #self.spyFreeSlotTime = datetime(2000, 1, 1)
        self.freeSlotToWait = self.config.slotsToReserve

        self.attackingShip = INGAME_TYPES_BY_NAME[self.config.attackingShip]
        if self.attackingShip.name == 'smallCargo':
            self.secondaryAttackingShip = INGAME_TYPES_BY_NAME['largeCargo']
        else:
            self.secondaryAttackingShip = INGAME_TYPES_BY_NAME['smallCargo']

    def run(self):
        ResetTimes = 0
        while True:
            try:
                self._checkThreadQueue()
                self._connect()              
                self._start()
            except (BotReset):
                print 'Get BotReset except'
                ResetTimes = ResetTimes + 1
                if ResetTimes > 5:
                    self.stop()
                    print 'Avoid Dead Loop'
                    break
                else:
                    continue
            except (KeyboardInterrupt, SystemExit, ManuallyTerminated, BotFatalError):
                self.stop()
                print "Bot stopped."              
                break
            except BotFatalError, e:
                self.stop()
                self.eventMgr.fatalException(e)
                break
            except BotError, e:
                self.eventMgr.activityMsg("Error: %s" % e)
            except Exception:
                traceback.print_exc()
                self.eventMgr.activityMsg("Something unexpected occured, see log file. Restarting bot.")
                self.eventMgr.activityMsg(traceback.format_exc())
                self.stop()
                break
            self._checkThreadQueue()
            mySleep(5)
    
    def stop(self):
        if self.web:
            self.web.saveState()
  
         
    def _connect(self):
        self.eventMgr.activityMsg("Contacting server...")
        self.web = WebAdapter(self.config, self._checkThreadQueue, self.gui)
        self.web.doLogin()
        self.myPlanets = self.web.getMyPlanets()
        print "#########"
        print  self.myPlanets
        self.researchLevels = self.web.getResearchLevels()

        #print self.researchLevels
    
        ratio_tech = [10,10,10,20,20,30,20,10,10,20,0,30,30,30]

        if self.researchLevels.has_key('impulseDrive') and self.researchLevels['impulseDrive'] >= 5:
            	INGAME_TYPES_BY_NAME['smallCargo'].speed=10000;
            	ratio_tech[0]=20;

        # bombers
        if self.researchLevels.has_key('hyperspaceDrive') and self.researchLevels['hyperspaceDrive'] >= 8:
            INGAME_TYPES_BY_NAME['bomber'].speed=5000;
            ratio_tech[9]=30;
        
        for i in range(14) :
            if ratio_tech[i] == 10 :
                INGAME_TYPES[i].speed = int(INGAME_TYPES[i].speed+self.researchLevels['combustionDrive']*INGAME_TYPES[i].speed*0.1);
            elif ratio_tech[i] == 20 and self.researchLevels.has_key('hyperspaceDrive'):
                INGAME_TYPES[i].speed = int(INGAME_TYPES[i].speed+self.researchLevels['impulseDrive']*INGAME_TYPES[i].speed*0.2);
            elif ratio_tech[i] == 30 and self.researchLevels.has_key('hyperspaceDrive'):
                INGAME_TYPES[i].speed = int(INGAME_TYPES[i].speed+self.researchLevels['hyperspaceDrive']*INGAME_TYPES[i].speed*0.3);

        #largeCargos = INGAME_TYPES_BY_NAME['largeCargo']
        #smallCargos = INGAME_TYPES_BY_NAME['smallCargo']
        
        ownedCoords = [repr(planet.coords) for planet in self.myPlanets]
        for coords in self.config.sourcePlanets + [self.config.deuteriumSourcePlanet]:
            if coords and str(coords) not in ownedCoords:
                raise BotFatalError("You do not own one or more planets you entered.")        
        
        for planet in self.myPlanets:
            if planet.coords in self.config.sourcePlanets:
                self.sourcePlanets.append(planet)
        if not self.sourcePlanets:
            self.sourcePlanets.append(self.myPlanets[0]) 
        self.eventMgr.connected()            


    def _start(self):
        self.loadFiles()
        nextCheckTime = datetime(2000, 1, 1)
        nextSaveTime = datetime(2000, 1, 1)
        planetsToAttack = []
        #Init self.myPlanetsAvaliable
        self.myPlanetsAvaliableTime = dict([(p.coords, self.web.serverTime()) for p in self.myPlanets])
        print 'self.myPlanetsAvaliableTime', self.myPlanetsAvaliableTime
        # remove planets to avoid from target list
        for planet in self.inactivePlanets[:]:
            if planet.owner in self.config.playersToAvoid or planet.alliance in self.config.alliancesToAvoid:
                self.inactivePlanets.remove(planet)
        #self.saveFiles() 
                            
        # generate reachable solar systems list
        attackRadius = int(self.config.attackRadius)
        newReachableSolarSystems = [] # contains tuples of (galaxy,solarsystem)
        for sourcePlanet in self.sourcePlanets:
            galaxy = sourcePlanet.coords.galaxy
            firstSystem = max(1, sourcePlanet.coords.solarSystem - attackRadius)
            lastSystem = min(int(self.config.systemsPerGalaxy), sourcePlanet.coords.solarSystem + attackRadius)
            for solarSystem in range(firstSystem, lastSystem +1):
                tuple = (galaxy, solarSystem)
                if tuple not in newReachableSolarSystems:
                    newReachableSolarSystems.append(tuple)
                
        if newReachableSolarSystems != self.reachableSolarSystems: # something changed in configuration (attack radius or attack sources)
            while (len(self.reachableSolarSystems)):
                self.reachableSolarSystems.pop()
            #for solarSystem in self.reachableSolarSystems:
            #    self.reachableSolarSystems.remove(solarSystem)
            for solarSystem in newReachableSolarSystems:
                self.reachableSolarSystems.append(solarSystem)
            # remove planets that are not in range anymore
            for planet in self.inactivePlanets[:]:
                if (planet.coords.galaxy, planet.coords.solarSystem) not in self.reachableSolarSystems:
                    self.inactivePlanets.remove(planet)
            #self.saveFiles() 
            self.inactivePlanetsByCoords = dict([(str(p.coords), p) for p in self.inactivePlanets])
            #if the range is changed, just update the inactivePlanets and the inactivePlanetsByCoords,
            #the new scan will be performed before spy.
            #self.scanGalaxies()

        if self.lastInactiveScanTime == 0:
            self.eventMgr.activityMsg("First run - Galaxie Scan")
            self.scanGalaxies()
            
        if not self.inactivePlanets:
            self.eventMgr.activityMsg("First run - Galaxie Scan")
            self.scanGalaxies()

        self.updateSourcePlanetsDict()
        self.eventMgr.activityMsg("Bot started.")
        self.inactivePlanetsByCoords = dict([(str(p.coords), p) for p in self.inactivePlanets])
        rentabilities = self.generateRentabilityTable(self.inactivePlanets)
        self.eventMgr.simulationsUpdate(rentabilities)
        
        # main loop:
        #How the main loop work?
        #Two lists        
        #planetsToSpy = []
        #planetsToAttack = [] are used
        #After getPlanetsToSpy, the planetsToSpy should never be empty, at least contain the planet in the top of the rentabilities
        #spyPlanets should never return before finishing all planetsToSpy, but attackPlanets should return when fleetlimit hit or
        #it will take too long to loop to auto research and build.
        while True:
            serverTime = self.web.serverTime()
            self._checkThreadQueue()
            #print 'severTime', serverTime
            
            if self._isInPause(serverTime) :
                self.eventMgr.statusMsg("Paused")
                sleep(1)
                continue
                
            if (serverTime.time() > self.config.preMidnightPauseTime or serverTime.time() <= self.config.inactivesAppearanceTime):
                self.eventMgr.statusMsg("Pre-midnight pause")
                sleep(60)
                continue
            #Update the inactivePlanets everyday to avoid miss attack and ban
            #Daliy update not done here, (-1,+1)galaxy will update before spy
            #            if (self.lastInactiveScanTime.date() != serverTime.date() and serverTime.time() >  self.config.inactivesAppearanceTime):
            #                self.scanGalaxies()
            #                self.spyPlanets(self.inactivePlanets, EspionageReport.DetailLevels.buildings)

            #Now all things are ready
            if (serverTime > nextSaveTime):
                nextSaveTime = serverTime + timedelta(0, 60)
                self.saveFiles() 
            #Update Resources on my planets
            if (serverTime > nextCheckTime):
                print '###Now update res and building',serverTime, nextCheckTime
                nextCheckTime = serverTime + timedelta(0, 2120)
                print '###Now timely update'
                self.web.getAndUpdateMyPlanetsResources()
                #self.gui.myPlanetsResTableUpdate(self.myPlanetsResources)
                #Check the attacking list and return list to see if any serious issue happen                

            #New inactive planets will appear now
            if (self.lastInactiveScanTime.date() != serverTime.date() and serverTime.time() >  self.config.inactivesAppearanceTime):
                for p in self.newScanedPlanets:
                    self.newScanedPlanets.remove(p)
                print 'self.lastInactiveScanTime',self.lastInactiveScanTime
                self.lastInactiveScanTime = self.web.serverTime()
                #print 'new self.lastInactiveScanTime',self.lastInactiveScanTime
                
            #Spy and decide to attack a planet
            planetsToSpy = []
            
            if not planetsToAttack: #still some plantes in list planetsToAttack, no need to spy
                planetsToSpy = self.getPlanetsToSpy()
                #print 'planetsToSpy', planetsToSpy
                
                if not planetsToSpy:
                    BotFatalError("Incorrect in planetsToSpy")
                    
                planetsToAttack = self.getPlanetsToAttack(planetsToSpy)
                #print 'planetsToAttack before attackPlanets', planetsToAttack
            
            if planetsToAttack:
                planetsToAttack = self.attackPlanets(planetsToAttack)
            #print 'planetsToAttack after attackPlanets', planetsToAttack
            
            #old = self.getInactiveInSolarSystem('2',['125','126', '127', '128', '129'])
            #new = self.scanInactiveInSolarSystem('2',['125','126', '127', '128', '129'])
            #temp = self.updateInactiveInSolarSystem(old, new)

            #attack the planet
            #sleep and continue loop                
            mySleep(1)
            #here to check if any message in ThreadQueue

            
    def mvToDeuteriumSourcePlanet(self):
        if self.config.deuteriumSourcePlanet:
            deuteriumSourcePlanet = (p for p in self.myPlanets if p.coords == self.config.deuteriumSourcePlanet).next()
        else:
            deuteriumSourcePlanet = None
            
        if deuteriumSourcePlanet:
            self.web.goToPlanet(deuteriumSourcePlanet)
        else:
            self.web.selectDeuteriumSourcePlanet()    


    def getPlanetsToSpy(self):
        #from WebAdapter import parseTime
        planetsToSpy = []
        seedPlanets = []
        serverTime = self.web.serverTime()

        self.mvToDeuteriumSourcePlanet()
        rentabilities = self.generateRentabilityTable(self.inactivePlanets)
        
        for index in xrange(0, min(len(rentabilities), random.randint(1, 5))):
            seedPlanets.append(rentabilities[index])
            
        maxAdd = 3
        for index in range (0, len(rentabilities)):
            report = rentabilities[index].targetPlanet.getBestEspionageReport()
            if report:
                if report.isDefended() and rentabilities[index].targetPlanet.espionageHistory[-1].hasExpired2(serverTime) and maxAdd > 0:
                    maxAdd = maxAdd - 1
                    seedPlanets.append(rentabilities[index])
            
        for seedPlanet in seedPlanets[:]:
            solarSystems = []
            tempNewScanedPlanets = []
            #seedPlanet = rentabilities[0]
            solarSystems.append(str(seedPlanet.targetPlanet.coords.solarSystem))
            
            #new scan solarSystem should in range defined in config file
            attackRadius = int(self.config.attackRadius)
            firstSystem = max(1, seedPlanet.sourcePlanet.coords.solarSystem - attackRadius)
            lastSystem = min(int(self.config.systemsPerGalaxy), seedPlanet.sourcePlanet.coords.solarSystem + attackRadius)
            #print 'firstSystem', firstSystem
            #print 'lastSystem', lastSystem
            pre_num = random.randint(1,5)
            post_num = random.randint(1,5)
            #print 'pre_num,post_num', pre_num, post_num
        
            for p in range(1, pre_num):
                if (seedPlanet.targetPlanet.coords.solarSystem +p) <= lastSystem:
                    solarSystems.append(str(seedPlanet.targetPlanet.coords.solarSystem+p))
            for p in range(1, post_num):
                if (seedPlanet.targetPlanet.coords.solarSystem-p) >= firstSystem:
                    solarSystems.append(str(seedPlanet.targetPlanet.coords.solarSystem-p))
        
            import time
            tempTime = time.strftime('%Y-%m-%d %H:%M:%S')

            for p in xrange(len(self.newScanedPlanets)):
                tempNewScanedPlanets.append(self.newScanedPlanets[p])
            
            if tempNewScanedPlanets:
                #print 'len', len(self.newScanedPlanets)
                #print 'self.newScanedPlanets', self.newScanedPlanets
                for p in xrange(len(tempNewScanedPlanets)):
                    #print 'self.newScanedPlanets[p]',self.newScanedPlanets[p]
                    galaxy, solar, updatetime = tempNewScanedPlanets[p]
                    if str(galaxy) == str(seedPlanet.targetPlanet.coords.galaxy) and str(solar) in solarSystems:
                        #print 'Current time', datetime.now()
                        #print 'strptime', time.strptime(updatetime, '%Y-%m-%d %H:%M:%S')
                        tmpstr = time.strptime(updatetime, '%Y-%m-%d %H:%M:%S')
                        #print 'datetime', datetime(tmpstr[0], tmpstr[1], tmpstr[2], tmpstr[3], tmpstr[4], tmpstr[5])
                        if datetime(tmpstr[0], tmpstr[1], tmpstr[2], tmpstr[3], tmpstr[4], tmpstr[5]) + timedelta(0, 7200) > datetime.now():
                            solarSystems.remove(solar)
                        else:
                            self.newScanedPlanets.remove((galaxy, solar, updatetime))

            if solarSystems:
                old = self.getInactiveInSolarSystem(str(seedPlanet.targetPlanet.coords.galaxy),solarSystems)
                new = self.scanInactiveInSolarSystem(str(seedPlanet.targetPlanet.coords.galaxy),solarSystems)
                self.updateInactiveInSolarSystem(old, new)
                for p in solarSystems:
                    self.newScanedPlanets.append((str(seedPlanet.targetPlanet.coords.galaxy),str(p),tempTime))
            
            for q in rentabilities:
                if not q.targetPlanet.espionageHistory: 
                    planetsToSpyByCoords = dict([(str(p.coords), p) for p in planetsToSpy]) 
                    if str(q.targetPlanet.coords) not in planetsToSpyByCoords:
                        planetsToSpy.append(q.targetPlanet)
                    
            planetsToSpyByCoords = dict([(str(p.coords), p) for p in planetsToSpy]) 
                
            if str(seedPlanet.targetPlanet.coords) not in planetsToSpyByCoords:
                planetsToSpy.append(seedPlanet.targetPlanet)
    
        #print 'planetsToSpy', planetsToSpy
        return planetsToSpy
        
    def getPlanetsToAttack(self, planetToSpyList):
        planetsToAttack = []
        planetToSpyByCoords = dict([(str(p.coords), p) for p in planetToSpyList])
        
        #if it is a buggy planet, just not to spy/attack again
        for p in self.planetsMayError:
            if str(p.coords) in planetToSpyByCoords:
                if p in planetToSpyList:
                    planetToSpyList.remove(p)
        
        if not planetToSpyList:
            return planetsToAttack
        
        self.spyPlanets(planetToSpyList, EspionageReport.DetailLevels.buildings)
        rentabilities = self.generateRentabilityTable(self.inactivePlanets)
        #Add rentabilities[0] and [1] after spy into planetsToAttack
        #print 'rentabilities[0].targetPlanet.coords', rentabilities[0].targetPlanet.coords
        #print 'rentabilities[1].targetPlanet.coords', rentabilities[1].targetPlanet.coords        
        #Now Search all planet in SpyList, if in range rentabilities [0,len(planetToSpyList)-1]

        
        for i in xrange(min(len(planetToSpyList), len(rentabilities))):
            #print 'len(rentabilities)', len(rentabilities)
            #print 'str(rentabilities[i].targetPlanet.coords)', str(rentabilities[i].targetPlanet.coords) 
            if (str(rentabilities[i].targetPlanet.coords) in planetToSpyByCoords) and (rentabilities[i] .rentability) > 0:
                planetsToAttack.append(rentabilities[i].targetPlanet)
        #print 'planetsToAttack', planetsToAttack
        return planetsToAttack

    def _isInPause(self, serverTime) :
        """
            Check the config to see if the bot should be in pause
        """
        for start, end in self.config.pausesTimes :
            additionalTime = random.randint(0, int(self.config.randomPauseTime))

            hour = end.hour + additionalTime/3600
            minute = end.minute + (additionalTime%3600)/60
            second = end.second + ((additionalTime%3600)%60)
            start.replace(hour,minute,second)

            additionalTime = random.randint(0, int(self.config.randomPauseTime))
            hour = end.hour + additionalTime/3600
            minute = end.minute + (additionalTime%3600)/60
            second = end.second + ((additionalTime%3600)%60)
            end.replace(hour,minute,second)
            if serverTime.time() >= start and serverTime.time() < end :
                return True
        return False
    
    def scanGalaxies(self):
        self.eventMgr.activityMsg("Searching inactive planets in range (this may take up to some minutes)...")

        startTime = datetime.now()
        self.mvToDeuteriumSourcePlanet()
        
        allInactives = []
        for galaxy, solarSystem, foundPlanets in self.web.getSolarSystems(self.reachableSolarSystems):
            for planet in foundPlanets:
                if 'inactive' == planet.ownerStatus and planet.owner not in self.config.playersToAvoid and planet.alliance not in self.config.alliancesToAvoid:
                    allInactives.append(planet)

        #seconds = (datetime.now() - startTime).seconds
        #systems = len(self.reachableSolarSystems)
        
        #self._planetDb.writeMany(allInactives)
        #allInactives = [p for p in allInactives if 'inactive' in p.ownerStatus and p.owner not in self.config.playersToAvoid and p.alliance not in self.config.alliancesToAvoid]
                
        newInactives = []
        for newPlanet in allInactives:
            oldPlanet = self.inactivePlanetsByCoords.get(str(newPlanet.coords))
            if oldPlanet:
                newPlanet.espionageHistory = oldPlanet.espionageHistory
                newPlanet.simulation = oldPlanet.simulation
            else:
                if __debug__: print >>sys.stderr, "New inactive planet found: " + str(newPlanet)
                newInactives.append(newPlanet)

        self.lastInactiveScanTime = self.web.serverTime()
        for p in xrange(len(newInactives)):
            self.inactivePlanets.append(newInactives[p])
        self.inactivePlanetsByCoords = dict([(str(p.coords), p) for p in self.inactivePlanets])              
        self.updateSourcePlanetsDict()

        systems=len(self.reachableSolarSystems)
        self.eventMgr.activityMsg("Inactive search finished. %s inactives (%s new) found in %s solar sytems." % (len(allInactives), len(newInactives), systems))
        
        #newInactives.save(FILE_PATHS['newinactives'])
        
        if not self.inactivePlanets:
            raise BotFatalError("No inactive planets are found,please reset the range")
            
        self.saveFiles()
        #print 'self.inactivePlanets', self.inactivePlanets
        return newInactives

    def getInactiveInSolarSystem(self, galaxyToGet, solarSystemToGetList):
        inactivePlanets = []
        for planet in self.inactivePlanets[:]:
            #print 'planet.coords.galaxy,planet.coords.solarSystem', planet.coords.galaxy,planet.coords.solarSystem
            if planet.coords.galaxy == int(galaxyToGet) and str(planet.coords.solarSystem) in solarSystemToGetList:
                inactivePlanets.append(planet)
        return inactivePlanets
        
        
    def updateInactiveInSolarSystem(self, inactivePlanetsOld, inactivePlanetsNew):
        newInactives = PlanetList()
        #inactivePlanets = []
        #for p in self.inactivePlanets:
        #    inactivePlanets.append(p)
        self.inactivePlanetsByCoords = dict([(str(p.coords), p) for p in self.inactivePlanets]) 
        #print 'inactivePlanets', inactivePlanets
        #print 'inactivePlanetsOld', inactivePlanetsOld
        #print 'inactivePlanetsNew', inactivePlanetsNew
        
#Why these stupid code added?It is can not work well in Chinese sever to judge wether planet is in inactivePlanets
        inactivePlanetsOldByCoords = dict([(str(p.coords), p) for p in inactivePlanetsOld])
        inactivePlanetsNewByCoords = dict([(str(p.coords), p) for p in inactivePlanetsNew])
        inactivePlanetsOldTmp = [p.coords for p in inactivePlanetsOld]
        inactivePlanetsNewTmp = [p.coords for p in inactivePlanetsNew]

        for planetCoord in inactivePlanetsOldTmp:
            if planetCoord not in inactivePlanetsNewTmp:
                self.inactivePlanets.remove(inactivePlanetsOldByCoords.get(str(planetCoord)))
#        self.saveFiles()         
#Keep the planet already in list and do nothing to them        
#        for planetCoord in inactivePlanetsOldTmp:
#            if planetCoord in inactivePlanetsNewTmp:

        for planetCoord in inactivePlanetsNewTmp:
            if planetCoord not in inactivePlanetsOldTmp:
                newInactives.append(inactivePlanetsNewByCoords.get(str(planetCoord)))
                self.inactivePlanets.append(inactivePlanetsNewByCoords.get(str(planetCoord)))
                
        #print 'inactivePlanets', inactivePlanets
        #for p in self.inactivePlanets :
        #    self.inactivePlanets.remove(p)
        #for p in inactivePlanets:
        #    self.inactivePlanets.append(p)
#update the dict and update rentabilities table on gui
        self.inactivePlanetsByCoords = dict([(str(p.coords), p) for p in self.inactivePlanets])              
        self.updateSourcePlanetsDict()
        rentabilities = self.generateRentabilityTable(self.inactivePlanets)
        self.eventMgr.simulationsUpdate(rentabilities)
        #self.saveFiles() 
        
        return newInactives
        
    def scanInactiveInSolarSystem(self, galaxyToScan, solarSystemToScanList):
        inactivePlanets = []
        solarSystems = []
        #print 'galaxyToScan, solarSystemToScan', galaxyToScan, solarSystemToScan
        for solarSystemToScan in solarSystemToScanList:
            solarSystems.append((int(galaxyToScan), solarSystemToScan))
        
        #if self.config.deuteriumSourcePlanet:
        #    deuteriumSourcePlanet = (p for p in self.myPlanets if p.coords == self.config.deuteriumSourcePlanet).next()
        #else:
        #    deuteriumSourcePlanet = None
        
        tmpPlanet = self.web.getSolarSystems(solarSystems)
        if not tmpPlanet:
            return inactivePlanets
            
        for galaxy, solarSystem, foundPlanets in tmpPlanet:
            for planet in foundPlanets:
                inactivePlanets.append(planet)
                        
        inactivePlanets = [p for p in inactivePlanets if 'inactive' == p.ownerStatus and p.owner not in self.config.playersToAvoid and p.alliance not in self.config.alliancesToAvoid]
        
        return inactivePlanets
        
    
    def attackPlanets(self, planetsToAttackList):
        planetsToAttack = []
        remainingPlanets = []
        #selectedAttacks = []
        
        remainingPlanets = [(self.sourcePlanetsDict[p], p) for p in planetsToAttackList]
        
        self.freeSlotToWait = self.config.slotsToReserve
        
        while True:
            serverTime = self.web.serverTime()
            rentabilities = self.generateRentabilityTable(self.inactivePlanets)
            self.eventMgr.simulationsUpdate(rentabilities)
            self._checkThreadQueue()
                    
            # update list of flying attacks that have not yet arrived to target planet
            for attack in self.notArrivedAttacks:
                if serverTime > attack.arrivalTime + timedelta(0, 20):
                    self.notArrivedAttacks.remove(attack)

            if not remainingPlanets:
                break
                    
            #for p in remainingPlanets:
                #selectedAttacks.append(p)
            
            # wait until a slot gets free
            if self.freeSlotTime < self.web.serverTime():
                source, target = remainingPlanets[0]
                #print 'In attack in loop check,self.freeSlotTime=', self.freeSlotTime
                #print 'self.web.serverTime()', self.web.serverTime()
                if self.myPlanetsAvaliableTime[source.coords] > self.web.serverTime():
                    break
                
                #print 'rentabilityTreshold=',rentabilityTreshold
                try:                    
                    sentMission = self.attackUndefendedPlanet(source, target, self.attackingShip)
                    # fleet has been send, drop the extra-slots limitations
                    self.freeSlotToWait = self.config.slotsToReserve
                    waitingForFreeSlots = False
                except NotEnoughShipsError, e:
                    self.eventMgr.activityMsg("Not enough ships for attack mission from %s to %s. %s" %(source, target, e))
                    if e.flyingMissions:
                        findEnoughShip, availableTime = self.treateNoFleetError(e, source, 'attack')
                        if findEnoughShip == True:
                            self.myPlanetsAvaliableTime[source.coords] = availableTime
                            print 'self.myPlanetsAvaliableTime[source]', self.myPlanetsAvaliableTime[source.coords]
                        else:
                            remainingPlanets.remove((source,target))
                            self.planetsMayError.append(target)
                            self.inactivePlanets.remove(target)
                            #self.saveFiles()
                    else:
                        remainingPlanets.remove((source,target))
                        self.planetsMayError.append(target)
                        self.inactivePlanets.remove(target)
                        #self.saveFiles()
                    break
                except NotEnoughDeutError, e:
                    self.eventMgr.activityMsg("Not enough deut for attack mission from %s to %s. %s" %(source, target, e))
                    self.myPlanetsAvaliableTime[source.coords] = self.web.serverTime() + timedelta(0, 300)
                    break
                    #remainingPlanets.remove((source,target))
                    #self.planetsMayError.append(target)
                except NoFreeSlotsError, e:
                    self.eventMgr.statusMsg("Fleet limit hit")
                    self.freeSlotTime = self.web.serverTime()
                    #print 'e.flyingMissions', e.flyingMissions
                    tempTime = datetime(2100, 1, 1) #give a new time for init value
                    for mission in e.flyingMissions :
                        if tempTime > mission.returnTime:
                            tempTime = mission.returnTime
                    self.freeSlotTime = tempTime
                    print 'self.freeSlotTime in attack', self.freeSlotTime
                    break
                    #print 'In attack when reach fleet limit,self.freeSlotTime=', self.freeSlotTime
                    #print 'self.web.serverTime()', self.web.serverTime()
                    # we have hit the slots limit
                except FleetSendError, e:
                    self.eventMgr.activityMsg("Error sending attack mission to planet %s. Reason: %s" %(target, e))
                    remainingPlanets.remove((source,target))
                    self.planetsMayError.append(target)
                    self.inactivePlanets.remove(target)
                    #self.saveFiles()
                    #No need to break as it do not need to wait
                else:
                    remainingPlanets.remove((source,target))
                    self.notArrivedAttacks.append(sentMission)
            mySleep(1)
        
        for p in remainingPlanets:
            source, target = p
            planetsToAttack.append(target)
        return planetsToAttack

    def treateNoFleetError(self, error,sourcePlanet, missionType):
        shipType, requested = (error.requested.items())[0]
        shipNo =error.available
        findEnoughShip = False
        availableTime = None
  
        if missionType == 'attack' :
            missionType = Mission.Types.attack
        elif  missionType == 'spy' :
            missionType = Mission.Types.spy
        else:
            missionType = Mission.Types.unknown
  
        for mission in error.flyingMissions :
            if mission.missionType == missionType and mission.sourcePlanet.coords == sourcePlanet.coords:
                ShipNo = mission.fleet[shipType] + shipNo
                if ShipNo >= requested:
                    findEnoughShip = True
                    #print 'ShipNo', ShipNo
                    break
                    
        if findEnoughShip == True:
            availableTime = mission.returnTime + timedelta(0, 10)
        
        return (findEnoughShip, availableTime)

    def spyPlanets(self, planetsToSpy, upToDetailLevel = None):
        if not planetsToSpy:
            return
            
        sentSpyTasks = 0
        nextCheckTime = self.web.serverTime()
        spyFreeSlotTime = self.web.serverTime()
        remainingPlanets = [(self.sourcePlanetsDict[p], p) for p in planetsToSpy]
        remainingPlanets.sort(key=lambda x:x[1].coords) # sort by target coords
        remainingPlanets.sort(key=lambda x:x[0]) # sort by source planet        

        while True:
            #self.eventMgr.simulationsUpdate(self.generateRentabilityTable(self.inactivePlanets))
            self._checkThreadQueue()
            serverTime = self.web.serverTime()

            if nextCheckTime < serverTime:
                self.checkIfEspionageReportsArrived()
                nextCheckTime = serverTime + timedelta(0, 15)
                if nextCheckTime < spyFreeSlotTime:
                    nextCheckTime = spyFreeSlotTime
                #print 'NextCheck Time for spy result is', nextCheckTime
                                    
            for source, target in remainingPlanets[:]:
                #if (target.espionageHistory and target.getBestEspionageReport().hasAllNeededInfo(upToDetailLevel) and not target.espionageHistory[-1].hasExpired(serverTime)) or target not in self.inactivePlanets:
                if (target.espionageHistory and not target.espionageHistory[-1].hasExpired(serverTime)):
                    remainingPlanets.remove((source, target))
    
            if not remainingPlanets and not self._notArrivedEspionages: #Now finish all spy and get all report
                break
            
            possibleTargets = [x for x in remainingPlanets]
            if not possibleTargets: #Now finished send to spy all remainingPlanets, just wait to check the report
                mySleep(1)
                continue

            source, target = possibleTargets[0]
            #print source
            #print target
            if self.myPlanetsAvaliableTime[source.coords]  > self.web.serverTime():
                mySleep(1)
                continue
                
            msg = "Spying"
            probesToSend = 0            
            if upToDetailLevel == None:
                probesToSend = int(self.config.probesToSend)
            elif upToDetailLevel == EspionageReport.DetailLevels.resources:
                probesToSend = int(self.config.probesToCheck)
            else:
                if not target.espionageHistory:
                    # send no. of probes equal to the average no. of probes sent until now.
                    probes = [planet.getBestEspionageReport().probesSent for planet in self.inactivePlanets if planet.espionageHistory]
                    if probes and len(probes) > 20:
                        probesToSend = max (int(sum(probes) / len(probes)), int(self.config.probesToSend))
                    else: 
                        probesToSend = int(self.config.probesToSend)
                    msg = "Spying for the 1st time"                            
                else:
                    probesToSend = target.getBestEspionageReport().probesSent    
                    if not target.getBestEspionageReport().hasAllNeededInfo(upToDetailLevel):
                        # we need to send more probes to get the defense or buildings data
                        msg = "Re-spying now "
                        probesToSend = max(int (1.5 * probesToSend), probesToSend +2) # non-linear increase in the number of probes
        
                samePlayerProbes = [planet.getBestEspionageReport().probesSent for planet in self.inactivePlanets if planet.espionageHistory and planet.owner == target.owner]

                probesToSend = max(samePlayerProbes + [probesToSend])
            
            if probesToSend > int(self.config.maxProbes):
                self.eventMgr.activityMsg("Espionage to planet %s would need too many probes. Skipping planet." % targetPlanet)
                remainingPlanets.remove((source, target))
                self.planetsMayError.append(target)
                self.inactivePlanets.remove(target)
                #self.saveFiles()
            else:
                if spyFreeSlotTime < self.web.serverTime():
                    try:
                        mission = self._spyPlanet(source, target, True, msg, probesToSend)
                    except NotEnoughShipsError, e:
                        self.eventMgr.activityMsg("Not enough esp ships for mission from %s to %s. %s" %(source, target, e))
                        if e.flyingMissions:
                            findEnoughShip, availableTime = self.treateNoFleetError(e, source, 'spy')
                            if findEnoughShip == True: 
                                self.myPlanetsAvaliableTime[source.coords] = availableTime
                                print 'self.myPlanetsAvaliableTime[source]', self.myPlanetsAvaliableTime[source.coords]
                        else:
                            remainingPlanets.remove((source,target))
                            self.planetsMayError.append(target)
                            self.inactivePlanets.remove(target)
                            #self.saveFiles()
                    except NoFreeSlotsError, e: 
                        self.eventMgr.statusMsg("Fleet limit hit")
                        spyFreeSlotTime = self.web.serverTime()
                        tempTime = datetime(2100, 1, 1)
                        for mission in e.flyingMissions :
                            if tempTime > mission.returnTime:
                                tempTime = mission.returnTime
                                #print 'tempTime', tempTime
                        spyFreeSlotTime = tempTime + timedelta(0, 3)
                        print 'self.freeSlotTime in spy', spyFreeSlotTime
                    except NotEnoughDeutError, e:
                        self.eventMgr.activityMsg("Not enough deut for esp mission from %s to %s. %s" %(source, target, e))
                        self.myPlanetsAvaliableTime[source.coords] = self.web.serverTime() + timedelta(0, 120)
                        #remainingPlanets.remove((source, target))
                        #self.planetsMayError.append(target)
                    except FleetSendError, e: 
                        self.eventMgr.activityMsg("Error sending esp mission to planet %s.Reason: %s" %(target, e))
                        remainingPlanets.remove((source, target))
                        if target in self.inactivePlanets:
                            self.inactivePlanets.remove(target)
                        #self.saveFiles() 
                        self.planetsMayError.append(target)
                    else: #if no error happens, move the planet to notArrivedEsp
                        remainingPlanets.remove((source, target))
                        self._notArrivedEspionages[target] = mission
                        sentSpyTasks = sentSpyTasks + 1
                        if sentSpyTasks < 5:
                            if nextCheckTime < mission.arrivalTime:
                                nextCheckTime = mission.arrivalTime + timedelta(0, 5)
                                print nextCheckTime
                        else:
                            nextCheckTime = serverTime
                            sentSpyTasks = 0
                            print 'changed NextCheck Time for spy result is', nextCheckTime
            mySleep(1)

    def _spyPlanet(self, sourcePlanet, targetPlanet, useReservedSlots=False, msg = "Spying", probesToSend = None):
        if useReservedSlots:
            slotsToReserve = 0
        else:
            slotsToReserve = self.config.slotsToReserve        
            
        if not probesToSend:
            probesToSend = int(self.config.probesToSend)
    
        ships = {'espionageProbe':probesToSend}
        mission = Mission(Mission.Types.spy, sourcePlanet, targetPlanet, ships)
        self.web.launchMission(mission, True, slotsToReserve)
        #self._notArrivedEspionages[targetPlanet] = mission
        self.eventMgr.activityMsg("%s %s from %s with %s" % (msg, targetPlanet, sourcePlanet, ships))
        #mySleep(0)
        return mission
    
    def attackUndefendedPlanet(self, sourcePlanet, targetPlanet, attackingShip, abortIfNoShips = True):
        resourcesToSteal = targetPlanet.simulation.simulatedResources.half()
        producedMeanwhile = targetPlanet.simulation.calculateProduction(sourcePlanet.coords.flightTimeTo(targetPlanet.coords, attackingShip.speed))
        ships = math.ceil((resourcesToSteal + producedMeanwhile).total() / float(attackingShip.capacity))

        if self.config.roundShipsNumber == True:  #Why original use int(self.config.roundShipsNumber) == 1 to judge
            shipsok = int(ships / 5)
            if shipsok < (ships / 5):
                ships = ((shipsok + 1)*5)
                rand = random.randint(0,100)
                if rand <= int(self.config.randomShipsNumber) :
                    ships = ships - 5
        else :
            rand = random.randint(0,100)
            if rand <= int(self.config.randomShipsNumber) :
                ships = ships - 1

        
        fleet = { attackingShip.name : ships }

        mission = self._attackPlanet(sourcePlanet, targetPlanet, fleet, abortIfNoShips, self.freeSlotToWait)

        shipsSent = mission.fleet[attackingShip.name]
        if shipsSent < ships:
            factor = shipsSent / float(ships)
            targetPlanet.simulation.simulatedResources -= resourcesToSteal * factor
            self.eventMgr.activityMsg("There were not enough ships for the previous attack. Needed %s but sent only %s" % (fleet, mission.fleet))
        else:
            targetPlanet.simulation.simulatedResources -= resourcesToSteal
        #self.saveFiles()
        #mySleep(20) This sleep is now removed to up level
        
        return mission

    def _attackPlanet(self, sourcePlanet, targetPlanet, attackingFleet, abortIfNoShips = True, slotToBeFree = 0):
        mission = Mission(Mission.Types.attack, sourcePlanet, targetPlanet, attackingFleet)

        self.web.launchMission(mission, abortIfNoShips, slotToBeFree)
        targetPlanet.attackHistory.append(mission)
        self.eventMgr.activityMsg("ATTACKING %s from %s with %s" % (targetPlanet, sourcePlanet, attackingFleet))
        return mission
        
        
    def checkIfEspionageReportsArrived(self):
        arrivedReports = []
        self.inactivePlanetsByCoords = dict([(str(p.coords), p) for p in self.inactivePlanets]) 
        
        if self._notArrivedEspionages:
            displayedReports = self.web.getEspionageReports()
            
            for planet, espionage in self._notArrivedEspionages.items():
                reports = [report for report in displayedReports if report.coords == espionage.targetPlanet.coords and report.date >= espionage.launchTime]
                reports.sort(key=lambda x:x.date, reverse=True) #To get the newest report of one planet
                if  reports:
                    report = reports[0]
                    report.probesSent = espionage.fleet['espionageProbe']
                    del self._notArrivedEspionages[planet]
                    
                    for p in self.inactivePlanetsByCoords:
                        if p == str(report.coords):
                            self.inactivePlanets.remove(self.inactivePlanetsByCoords.get(str(p)))
                            planet.simulation = ResourceSimulation(report.resources, report.buildings)
                            while (len(planet.espionageHistory) != 0):
                                planet.espionageHistory.pop()
                            planet.espionageHistory.append(report)
                            self.inactivePlanets.append(planet)
                            arrivedReports.append(report)
                            #self.saveFiles() 
                            self.inactivePlanetsByCoords = dict([(str(p.coords), p) for p in self.inactivePlanets])              
                            self.updateSourcePlanetsDict()
                            rentabilities = self.generateRentabilityTable(self.inactivePlanets)
                            self.eventMgr.simulationsUpdate(rentabilities)
                    #self._planetDb.write(planet)
                elif self.web.serverTime() > espionage.arrivalTime + timedelta(minutes=2):
                    # Trying to check for server lag
                    #check if probes are still flying
                    missions = self.web.getFlyingMissions()
                    found = False
                    for mission in missions :
                        if mission.missionType == Mission.Types.spy :
                            if mission.targetPlanet.coords.galaxy == planet.coords.galaxy and mission.targetPlanet.coords.solarSystem == planet.coords.solarSystem and mission.targetPlanet.coords.planet == planet.coords.planet :
                                found = True
                                break
                            if mission.sourcePlanet.coords.galaxy == planet.coords.galaxy and mission.sourcePlanet.coords.solarSystem == planet.coords.solarSystem and mission.sourcePlanet.coords.planet == planet.coords.planet :
                                found = True
                                break
                    if found :
                        continue
                    # probably due to buggy espionage report (containing only N;) or translation errors.
                    del self._notArrivedEspionages[planet]
                    try: 
                        self.inactivePlanets.remove(planet)
                    except ValueError: pass
                    #self.saveFiles()                         
                    self.eventMgr.activityMsg("Espionage report from %s never arrived. Deleting planet." % espionage.targetPlanet)
            #self.saveFiles() 
                        
        if arrivedReports:
            self.web.deleteMessages(arrivedReports)
        return arrivedReports
                    
    def generateRentabilityTable(self, planetList, negativeIfDefended = True):
        rentabilities = []

        for planet in planetList:
            sourcePlanet = self.sourcePlanetsDict[planet]
            rentability = planet.rentability(sourcePlanet.coords, self.attackingShip.speed, self.config.rentabilityFormula)
            rentabilities.append(Struct(sourcePlanet=sourcePlanet, targetPlanet=planet, rentability=rentability))
        rentabilities.sort(key=lambda x:x.rentability, reverse=True)
        return rentabilities
    
    
    def updateSourcePlanetsDict(self):
        for targetPlanet in self.inactivePlanets:
            self.sourcePlanetsDict[targetPlanet] = self._calculateNearestSourcePlanet(targetPlanet.coords)
  
    def _calculateNearestSourcePlanet(self, coords):
        tmpSourcePlanets = self.sourcePlanets
        minDistance = sys.maxint
        random.shuffle(tmpSourcePlanets)
        for sourcePlanet in tmpSourcePlanets:
            distance = sourcePlanet.coords.distanceTo(coords)
            if distance < minDistance:
                nearestSourcePlanet = sourcePlanet
                minDistance = distance        
        return nearestSourcePlanet
    
    def _calculateSteal(self, resourcesToSteal, attackingFleet):
        pass
        
    def _checkThreadQueue(self):
        state = 'running'
        while True:
            try:
                msg = self.msgQueue.get(state == 'paused')
            except Empty: 
                break
                  
            if   msg.type == GuiToBotMsg.stop:
                raise ManuallyTerminated()
            elif msg.type == GuiToBotMsg.pause:
                print "Bot paused."
                state = 'paused'
            elif msg.type == GuiToBotMsg.resume:
                print "Bot resumed."                              
                state = 'running'
            elif msg.type == GuiToBotMsg.config:
                # reload the config
                self.config.load()
            elif msg.type == GuiToBotMsg.scan:
                self.scanGalaxies()
                print "Bot resumed."
                state = 'running'
                
          
    def saveFiles(self):
        path = FILE_PATHS['gamedata']
        try:
            shutil.move(path, path + ".bak")
        except IOError: pass
        
        file = open(path, 'wb')
        pickler = cPickle.Pickler(file, 2)
        for i in [self.inactivePlanets, self.reachableSolarSystems, self.lastInactiveScanTime, 
                  self.config.webpage, self.config.username]:
                  pickler.dump(i)
        file.close()
        
    def loadFiles(self):
        path = FILE_PATHS['gamedata']
        try:
            file = open(path, 'rb')
            u = cPickle.Unpickler(file)
            self.inactivePlanets = u.load()
            self.reachableSolarSystems = u.load()
            self.lastInactiveScanTime = u.load()
            storedWebpage = u.load()
            storedUniverse = u.load()
            file.close()

            if (storedWebpage != self.config.webpage) and (storedWebpage != ('www.'+ self.config.webpage)):
            	raise BotError() # if any of those has changed, invalidate stored espionages
            
            # fix to reset old versions's botdata:

            for planet in self.inactivePlanets:
                dummy = planet.attackHistory                    
                if planet.espionageHistory:
                    dummy = planet.espionageHistory[-1].rawHtml
                    break
            
        except (EOFError, IOError, BotError, ImportError, AttributeError):
            try: file.close()            
            except UnboundLocalError: pass
            try: 
                shutil.move(path + ".bak", path) # try to restore backup
                self.loadFiles()
                return
            except IOError: pass      
      
            self.inactivePlanets = []
            self.reachableSolarSystems = []
            self.lastInactiveScanTime = 0
            self.eventMgr.activityMsg("Invalid or missing gamedata, spying planets.")
            try: os.remove(path)              
            except Exception : pass
     
    def runPlugin(self, plugin):
        execfile("plugins/%s.py" % plugin)

    
if __name__ == "__main__":

    parser = OptionParser()
    parser.add_option("-c", "--console", action="store_true", help="Run in console mode'")
    parser.add_option("-a", "--autostart", action="store_true", help="Auto start bot, no need to click Start button")
    parser.add_option("-w", "--workdir", help="Specify working directory (useful to run various bots at once). If not specified defaults to 'files'")    
    parser.add_option("-p", "--plugin", help="Run the specified plugin from the plugins folder, and exit.")    
    (options, args) = parser.parse_args()
    
    if options.workdir:
        dirPrefix = options.workdir
    else:
        dirPrefix = 'files'
        
    for key, path in FILE_PATHS.items():
        path = dirPrefix + '/' + path
        FILE_PATHS[key] = path
        try: os.makedirs (os.path.dirname(path))
        except OSError, e: pass

    for dir in ('plugins', 'debug', 'output'):
        try: os.makedirs (dir)
        except OSError, e: pass
            
    logging.getLogger().setLevel(logging.INFO)
    handler = logging.handlers.RotatingFileHandler(os.path.abspath(FILE_PATHS['log']), 'a', 100000, 10)
    handler.setLevel(logging.INFO)
    handler.setFormatter(logging.Formatter('%(message)s'))
    logging.getLogger().addHandler(handler)

    if options.plugin:
        bot = Bot()
        bot.runPlugin(options.plugin)
    elif options.console:
        bot = Bot()
        bot.run()
    else:
        from gui import guiMain
        guiMain(options)
        
